package co.com.smart.eagle.client.view.impl.smartduallist;


import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import co.com.smart.eagle.infrastructure.dto.duallist.DualListItem;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;


public class DualList<T extends DualListItem> extends Composite implements DualListView <T> {
	
  interface DualListUiBinder extends UiBinder<Widget, DualList<?>> {
  }

  private DualListUiBinder uiBinder = GWT.create(DualListUiBinder.class);

  @UiField protected Label lblTituloIzquierda;
  @UiField protected Label lblTituloDerecha;
  
  @UiField(provided = true)
  protected ListBox right;
  @UiField(provided = true)
  protected ListBox left;
  
  @UiField
  protected TextButton btnAnadirDer;
  @UiField
  protected TextButton btnAnadirTodoDer;
  @UiField
  protected TextButton btnAnadirIzq;
  @UiField
  protected TextButton btnAnadirTodoIzq;
  
  private Map<String,T> itemsDisponibles;
  private Map<String, T> itemsSeleccionados;

  public DualList() {  
	right = new ListBox(true);
	left = new ListBox(true);
	itemsDisponibles = new HashMap<String,T>();
	itemsSeleccionados = new HashMap<String,T>();
    initWidget(uiBinder.createAndBindUi(this));
    lblTituloDerecha.removeStyleName("gwt-Label");
    lblTituloIzquierda.removeStyleName("gwt-Label");
    init();
  }  
  
  private void init()
  {
	  
	  left.addDoubleClickHandler(new DoubleClickHandler() {
		
		@Override
		public void onDoubleClick(DoubleClickEvent event) {
			moveItems(left,right);				
		}
	});
	  
	  right.addDoubleClickHandler(new DoubleClickHandler() {
		
		@Override
		public void onDoubleClick(DoubleClickEvent event) {
			moveItems(right, left);				
		}
	});
	  
	  btnAnadirDer.addSelectHandler(new SelectHandler() {
		
		@Override
		public void onSelect(SelectEvent event) {
			moveItems(left, right);			
		}
	});
	  
	  btnAnadirTodoDer.addSelectHandler(new SelectHandler() {
		
		@Override
		public void onSelect(SelectEvent event) {
			moveAllItems(left, right);
		}
	});
	  
	  btnAnadirIzq.addSelectHandler(new SelectHandler() {
		
		@Override
		public void onSelect(SelectEvent event) {
			moveItems(right, left);			
		}
	});
	  
	  
	  btnAnadirTodoIzq.addSelectHandler(new SelectHandler() {
		
		@Override
		public void onSelect(SelectEvent event) {
			moveAllItems(right, left);
		}
	});
  }

  /**
   * 
   * @param item
   * @param value
   * @see DualList#addAvailableItems(Collection) to bulk operations.
   */
  public void addAvailableItem(String item, String value) {
    addItemWithTitle(left.getElement(),item,value );
    enableOrDisableButtons();
  }
  
  public void addSelectedtem(String item, String value) {
	    addItemWithTitle(right.getElement(),item,value );
	    enableOrDisableButtons();
  }


  /**
   * Clear both lists.
   */

  public void clearAvailableItems() {
    left.clear();
    itemsDisponibles.clear();
    enableOrDisableButtons();
  }

  public void clearSelectedItems() {
    right.clear();
    itemsSeleccionados.clear();
    enableOrDisableButtons();
  }

  private void enableOrDisableButtons() {
	  
	  if (right.getItemCount() == 0)
	  {
		    btnAnadirIzq.disable();
		    btnAnadirTodoIzq.disable();
	  }	  
	  else
	  {
		  btnAnadirIzq.enable();
		  btnAnadirTodoIzq.enable();
	  }
	  if (left.getItemCount() == 0)
	  {
		  btnAnadirDer.disable();
		  btnAnadirTodoDer.disable();
	  }
	  else
	  {
		  btnAnadirDer.enable();
		  btnAnadirTodoDer.enable();
	  }
  }

  private void moveItem(int index, ListBox source, ListBox target) {
    String value = source.getValue(index);
    
    if (itemsDisponibles.containsKey(value))
    {
    	itemsSeleccionados.put(value, itemsDisponibles.get(value));
    	itemsDisponibles.remove(value);
    }
    else
    {
    	itemsDisponibles.put(value, itemsSeleccionados.get(value));
    	itemsSeleccionados.remove(value);
    }    
    String item = source.getItemText(index);
    addItemWithTitle(target.getElement(),item, value);
    source.removeItem(index);
  }

  private void moveItems(final ListBox source, ListBox target) {
    int index = source.getSelectedIndex();
    if (index == -1)
    {
    	Window.alert("Seleccione un elemento de la lista");
		source.setFocus(true);
    }
    while (index != -1) {
      moveItem(index, source, target);
      index = source.getSelectedIndex();
    }
    enableOrDisableButtons();
  }

  private void moveAllItems(ListBox source, ListBox target)
  {
	  while (source.getItemCount() > 0)
	  {
		  moveItem(0, source, target);
	  }
	  enableOrDisableButtons();
  }
  
	@Override
	public void addItemsDisponibles(Collection<T> items) 
	{
		clearAvailableItems();
		checkSelectedItems(items);
		for (T item : items)
		{
			if (!itemsSeleccionados.containsKey(item.getIdItem()))
			{
				itemsDisponibles.put(item.getIdItem(), item);
				addAvailableItem(item.getNombre(), item.getIdItem());
			}			
		}		
		enableOrDisableButtons();
	}
	
	private void checkSelectedItems(Collection<T> newItems)
	{
		boolean found = false;
		
		Iterator<Entry<String, T>> iter = itemsSeleccionados.entrySet().iterator();		
		T selected = null;
		
		while (iter.hasNext())
		{	
			selected = iter.next().getValue();
			
			found = false;
			for (T newItem : newItems)
			{
				if (selected.getIdItem().equals(newItem.getIdItem()))
				{
					found = true;
					break;
				}
			}
			if (!found)
			{				
				right.removeItem(getIndexByValue(selected.getIdItem(), right));
				iter.remove();
			}
		}
	}
	
	@Override
	public void addItemSeleccionados(Collection<T> items) {	
		
		checkSelectedAvailable(items);
		for (T item : items)
		{
			if (!itemsSeleccionados.containsKey(item.getIdItem()))
			{
				if (itemsDisponibles.containsKey(item.getIdItem()))
				{
					left.removeItem(getIndexByValue(item.getIdItem(),left));
					itemsDisponibles.remove(item.getIdItem());				
					itemsSeleccionados.put(item.getIdItem(), item);
					addSelectedtem(item.getNombre(),item.getIdItem());
				}					
			}
			
		}		
		enableOrDisableButtons();
	}
	
	private void checkSelectedAvailable(Collection<T> selectedItems)
	{
		boolean found = false;		
		
		Iterator<Entry<String, T>> iter = itemsSeleccionados.entrySet().iterator();		
		T selected = null;
		
		while (iter.hasNext())
		{
			selected = iter.next().getValue();
			found = false;
			for (T newItem : selectedItems)
			{
				if (selected.getIdItem().equals(newItem.getIdItem()))
				{
					found = true;
					break;
				}
			}
			if (!found)
			{				
				right.removeItem(getIndexByValue(selected.getIdItem(), right));				
				iter.remove();
				itemsDisponibles.put(selected.getIdItem(), selected);
				addAvailableItem(selected.getNombre(),selected.getIdItem());
			}
		}
	}
	
	private int getIndexByValue(String key, ListBox listbox)
	{
		int i =  -1;
		for (int j = 0; i < listbox.getItemCount(); j++)
		{
			if (listbox.getValue(j).equals(key))
			{
				i = j;
				break;
			}
		}
		return i;
	}
	
	@Override
	public List<T> getItemsSeleccionados() {	
		return new ArrayList<T>(itemsSeleccionados.values());
	}
	
	@Override
	public void limpiar() 
	{
		clearAvailableItems();
		clearSelectedItems();
	}

	private static native void addItemWithTitle(Element element, String name, String value)/*-{
     var opt = $doc.createElement("OPTION");
     opt.title = name;
     opt.text = name;
     opt.value = value;
     element.options.add(opt);

 }-*/;
	
	@Override
	public void setTituloListaIzquierda(String texto)
	{
		lblTituloIzquierda.setText(texto);
	}
	
	@Override
	public void setTituloListaDerecha(String texto)
	{
		lblTituloDerecha.setText(texto);
	}

}
